1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.api.data.tilemap;
12 public import hip.api.renderer.texture;
13 public import hip.util.variant;
14 /**
15 *   TileLayers representations which can be found from Tiled.
16 */
17 enum TileLayerType
18 {
19     ///The Default one, a data structure holding tile mapping information.
20     TILE_LAYER   = "tilelayer",
21     ///A simple layer, containing only an image to be rendered above or below the tile layers.
22     IMAGE_LAYER  = "imagelayer",
23     ///A layer used only to hold data to be used within the gameplay implementation
24     OBJECT_LAYER = "objectgroup"
25 }
26 
27 enum TileDrawOrder : ubyte
28 {
29     TOP_DOWN,
30     DOWN_TOP
31 }
32 
33 /**
34 *   Simple Key/Value type defining a property inside a Tile.
35 */
36 struct TileProperty
37 {
38     string type;
39     Variant val;
40     pragma(inline, true) T get(T)()
41     {
42         static if(is(T == string)) return val.get!string;
43         else
44         {
45             if(val.type == Type.string_)
46                 val = Variant.make!T(val.get!string);
47             return val.get!T;
48         }
49     }
50     pragma(inline, true) T set(T)(T v) => val.set(v);
51 }
52 
53 enum TiledObjectTypes : ubyte
54 {
55     rect,
56     ellipse,
57     tile,
58     point,
59     text,
60     line,
61     triangle,
62     polygon
63 }
64 
65 
66 /**
67 *   A simple object which can mean absolutely anything inside a TileLayer.
68 *   Usually used for implementing:
69 -   Camera Dead Zones
70 -   Event Systems
71 -   Trigger Areas
72 
73     When wanting to get its following type, call either `rect`, `ellipse`, `tile`, `point`, `line`, `triangle`, `polygon` or `text`. Try to avoid calling
74     `text` multiple times since it involves 3 associative array accesses.
75 */
76 struct TiledObject
77 {
78     ushort id;
79     bool visible;
80     TiledObjectTypes dataType;
81     string name;
82     string type;
83 
84     TiledObjectUnion data;
85     TileProperty[string] properties;
86 
87     ref TiledRectangle rect() @trusted @nogc { assert(dataType == TiledObjectTypes.rect); return data.rect;}
88     ref TiledRectangle ellipse() @trusted @nogc { assert(dataType == TiledObjectTypes.ellipse); return data.rect;}
89     ref TiledRectangle tile() @trusted @nogc { assert(dataType == TiledObjectTypes.tile); return data.rect;}
90     ref int[2] point() @trusted @nogc { assert(dataType == TiledObjectTypes.point); return data.rect.getPoint();}
91     ref int[4] line() @trusted @nogc { assert(dataType == TiledObjectTypes.line); return data.rect.getLine();}
92     ref int[2][3] triangle() @trusted @nogc { assert(dataType == TiledObjectTypes.triangle); return data.triangle;}
93     ref int[2][] polygon() @trusted @nogc { assert(dataType == TiledObjectTypes.polygon); return data.polygon;}
94 
95     const(TiledText) text() @trusted
96     {
97         assert(dataType == TiledObjectTypes.text);
98         TileProperty* ff = "__fontFamily" in properties;
99         TileProperty* msg = "__text" in properties;
100         TileProperty* ww = "__wordWrap" in properties;
101 
102         return TiledText(data.rect, msg? msg.get!string : null, ff ? ff.get!string : null, ww ? ww.get!bool : false);
103     }
104 }
105 
106 struct TiledText
107 {
108     TiledRectangle rect;
109     string text;
110     string fontFamily;
111     bool wordWrap;
112 
113     alias rect this;
114 }
115 struct TiledRectangle
116 {
117     int x, y, width, height;
118     ushort rotation, gid;
119     ref int[2] getPoint() @trusted @nogc { return (cast(int*)&this)[0..2]; }
120     ref int[4] getLine() @trusted @nogc { return (cast(int*)&this)[0..4]; }
121 }
122 
123 union TiledObjectUnion
124 {
125     TiledRectangle rect;
126     int[2][3] triangle;
127     int[2][] polygon;
128 }
129 
130 
131 /**
132 *   The TileAnimationFrame holds what is the ID of the current frame in a TileAnimation and how much duration.
133 */
134 struct TileAnimationFrame
135 {
136     ushort id;
137     int duration;
138 }
139 
140 /**
141 *   The Tile is the smallest piece of a TileMap.
142 */
143 struct Tile
144 {
145     ///ID for backreferencing from TileMap
146     ushort id;
147     ///Which frame is in its animatoin
148     ushort currentFrame;
149     ///The tile texture region itself.
150     IHipTextureRegion region;
151     /**
152     *   Properties about the tile. Usually used for implementing gameplay stats such as:
153     -   Sounds
154     -   Collisions and Collidibles
155     -   Visual Effects
156     */
157     TileProperty[string] properties;
158     ///Frames for playing the tile animation
159     TileAnimationFrame[] animation;
160     alias properties this;
161 }
162 
163 /**
164 *   Tile Layer is a map of tiles consisting in a unique "texture" of tiles.
165 */
166 final class HipTileLayer
167 {
168     ///Layer name on the tilemap editor
169     string name;
170     ///Data
171     ushort[] tiles;
172     bool visible = true;
173     int x, y, width, height;
174     uint tileWidth, tileHeight;
175     ushort id;
176     string type;
177     string drawOrder;
178     TileProperty[string] properties;
179     TiledObject[] objects;
180     float opacity = 1.0;
181 
182     this(IHipTilemap map)
183     {
184         tileWidth = map.tileWidth;
185         tileHeight = map.tileHeight;
186     }
187 
188     /**
189     *
190     */
191     this(string name, uint width, uint height, ushort id, IHipTilemap map)
192     {
193         this(map);
194         this.name = name;
195         this.id = id;
196         this.width = width;
197         this.height = height;
198         tiles = new ushort[width*height];
199     }
200 
201     bool isInLayerBoundaries(int x, int y, int w, int h) const @nogc
202     {
203         int x2 = x+w;
204         int y2 = y+h;
205 
206         bool notInBoundariesX = x2 < this.x || x > this.width + this.x;
207         if(notInBoundariesX)
208             return false;
209         bool notInBoundariesY = y2 < this.y || y > this.height + this.y;
210 
211         return !notInBoundariesY;
212     }
213 
214     ///Expects I and J in column/row
215     pragma(inline, true)
216     ushort getTile(uint i, uint j) @nogc @safe
217     {
218        return tiles[j*width+i];
219     }
220     ushort checkedGetTile(uint i, uint j) @nogc @trusted
221     {
222         int target = j*width+i;
223         if(i >= width || j >= height || target < 0 || target >= tiles.length)
224             return 0;
225         return tiles.ptr[target];
226     }
227 
228     ///Gets tile from relative X and Y. Does not take into account the layer x, y
229     ushort getTileXY(uint x, uint y) @nogc @safe
230     {
231         return getTile(cast(uint)(x / tileWidth), cast(uint)(y / tileHeight));
232     }
233 
234     ///Gets tile from absolute X and Y. Takes into account the layer x, y
235     ushort checkedGetTileXY(int x, int y) @nogc @trusted
236     {
237         if(x < this.x || y < this.y || x > this.x+this.width || y > this.y+this.height)
238             return 0;
239         y-= this.y;
240         x-= this.x;
241         return checkedGetTile(x / tileWidth, y / tileHeight);
242     }
243     
244 }
245 
246 /**
247 *   Tileset is a data set containing tiles. It has information about the underlying texture used for a tile.
248 */
249 interface IHipTileset
250 {
251     uint columns() const;
252 
253     ///Means where the tileset id starts
254     uint firstGid() const;
255     
256 
257     ///"image" in tiled
258 
259     string texturePath() const;
260     ///"imageheight" in tiled
261     uint  textureHeight() const;
262     ///"imagewidth" in tiled 
263     uint  textureWidth() const; 
264 
265     IHipTexture texture();
266     int margin() const;
267     string name() const;
268 
269     ///Only available when loaded via .tsx
270     string path() const;
271     int spacing() const;
272     uint tileHeight() const;
273     uint tileWidth() const;
274 
275     ///Usually only accessed when looking for a specific property
276     Tile[] tiles();
277 
278     final uint tileCount()const {return cast(uint)(cast(IHipTileset)this).tiles.length;}
279     final IHipTextureRegion getTextureRegion(ushort id)
280     {
281         return tiles[id - firstGid].region;
282     }
283     final Tile* getTile(ushort id){return &tiles[id - firstGid];}
284 }
285 
286 
287 /**
288 *   Tilemap is a set of tile layers. It contains information on the maximum map size, holds all the tilesets needed for
289 *   actually drawing the layers.
290 */
291 interface IHipTilemap
292 {
293     @nogc ref int x();
294     @nogc ref int y();
295     @nogc ref HipColor color();
296     @nogc ref float scaleX();
297     @nogc ref float scaleY();
298 
299     ///Returns scaleX as the one to be modified.
300     @nogc float scale();
301     ///Modifies both scaleX and scaleY at the same time.
302     @nogc float scale(float v);
303     ref float rotation();
304     
305 
306     string path() const;
307     uint width() const @nogc;
308     uint height() const @nogc;
309     bool isInfinite() const @nogc;
310     ref HipTileLayer[string] layers();
311     string orientation() const @nogc;
312     string renderorder() const @nogc;
313     string tiled_version() const @nogc;
314 
315     uint tileHeight() const @nogc;
316     uint tileWidth() const @nogc;
317 
318     final uint tileWidthScaled() @nogc {return cast(uint)(scaleX * tileWidth);}
319     final uint tileHeightScaled() @nogc {return cast(uint)(scaleY * tileHeight);}
320 
321     void setTileSize(uint tileWidth, uint tileHeight);
322     ///Use it when programatically creating your tilemap
323     void addTileset(IHipTileset tileset);
324     ///Use it when programatically creating your tilemap
325     final HipTileLayer addNewLayer(string layerName, uint columns, uint rows)
326     {
327         return layers[layerName] = new HipTileLayer(layerName, columns, rows, cast(ushort)layers.length, this);
328     }
329     
330 
331     IHipTileset getTilesetForID(ushort id);
332     final IHipTextureRegion getTextureRegionForID(ushort id){return getTilesetForID(id).getTextureRegion(id);}
333     final Tile* getTileForID(ushort id){return getTilesetForID(id).getTile(id);}
334 
335 
336     alias layers this;
337 }